/*
XPTracker - Alternative Agile Tracking Tool
Copyright (C) 2006-2009  Stephen Starkey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package org.xptracker.domain;

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.IndexColumn;
import org.hibernate.annotations.Proxy;
import org.xptracker.persistence.Identified;

import javax.persistence.*;
import java.util.*;

@Entity
@Table(name = "queues")
@Proxy(lazy = false)
public class Queue extends Identified {
  private String name;
  private boolean open = false;
  private boolean deletable = true;
  private Boolean hidden = false;
  private QueueType type = QueueType.priority;
  private List<Story> stories = new LinkedList<Story>();
  private Set<User> trackers = new LinkedHashSet<User>();

  protected Queue() {
  }

  public Queue(String name) {
    this(name, false);
  }

  public Queue(String name, boolean open) {
    this.name = name;
    this.open = open;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public QueueType getType() {
    return type == null ? QueueType.priority : type;
  }

  public void setType(QueueType type) {
    this.type = type;
  }

  @OneToMany
  @JoinTable(name = "queue_stories", joinColumns = @JoinColumn(name = "queue_id"),
      inverseJoinColumns = @JoinColumn(name = "story_id"))
  @IndexColumn(name = "queue_idx")
  protected List<Story> getStoriesInternal() {
    return stories;
  }

  protected void setStoriesInternal(List<Story> stories) {
    this.stories = stories;
  }

  @Transient
  public List<Story> getStories() {
    return Collections.unmodifiableList(getStoriesInternal());
  }

  public void setStories(List<Story> stories) {
    setStoriesInternal(stories);
  }

  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    if (!super.equals(o)) return false;

    final Queue queue = (Queue) o;

    return !(name != null ? !name.equals(queue.name) : queue.name != null);
  }

  public int hashCode() {
    int result = super.hashCode();
    result = 29 * result + (name != null ? name.hashCode() : 0);
    return result;
  }

  public String toString() {
    return "Queue '" + name + "'";
  }

  public void add(Story story) {
    deletable = false;
    stories.add(story);
  }

  public void add(int index, Story story) {
    deletable = false;
    if (stories.isEmpty() || index > stories.size()) {
      add(story);
    } else {
      stories.add(index, story);
    }
  }

  public int remove(Story story) {
    List<Story> newStoryList = new LinkedList<Story>();

    int foundIndex = -1;
    boolean found = false;
    for (Story candidate : stories) {
      if (!found) {
        foundIndex++;
      }
      
      if (!candidate.equals(story)) {
        newStoryList.add(candidate);
      } else {
        found = true;
      }
    }

    this.stories = newStoryList;
    return foundIndex;
  }

  public boolean isDeletable() {
    return deletable;
  }

  public void setDeletable(boolean deletable) {
    this.deletable = deletable;
  }

  public boolean isOpen() {
    return open;
  }

  public void setOpen(boolean open) {
    this.open = open;
  }

  @ManyToMany(fetch=FetchType.EAGER)
  @JoinTable(name="queue_trackers", joinColumns = @JoinColumn(name="queue_id"),
        inverseJoinColumns = @JoinColumn(name="user_id"))
  @Fetch(FetchMode.JOIN)
  public Set<User> getTrackers() {
    return trackers;
  }

  public void setTrackers(Set<User> trackers) {
    this.trackers = trackers;
  }

  public void addTracker(User user) {
    if (user == null) {
      throw new IllegalArgumentException("User to add as tracker may not be null.");
    }
    trackers.add(user);
  }

  public boolean removeTracker(User user) throws IllegalStateException {
    return trackers.remove(user);
  }

  @Transient
  public boolean isHiddenPrimitive() {
    return isHidden();
  }

  public Boolean isHidden() {
    return hidden == null ? false : hidden;
  }

  public void setHidden(Boolean hidden) {
    this.hidden = (hidden == null ? false : hidden);
  }
}
